#include "No7pch.h"
#include "Shader.h"

#include <glad/glad.h>
#include <glm/gtc/type_ptr.hpp>

nmspc::Shader::~Shader()
{
	glDeleteProgram(this->program_id);
}

nmspc::Shader::Shader(const std::string& vertex_source_code, const std::string& fragment_source_code)
{
	this->program_id = 0;
	GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
	const GLchar* source = vertex_source_code.c_str();
	glShaderSource(vertex_shader, 1, &source, 0);
	glCompileShader(vertex_shader);
	GLint if_compiled = 0;
	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &if_compiled);
	if (if_compiled == GL_FALSE)
	{
		GLint max_length = 0;
		glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &max_length);
		std::vector<GLchar> info_log(max_length);
		glGetShaderInfoLog(vertex_shader, max_length, &max_length, &info_log[0]);
		glDeleteShader(vertex_shader);
		NO7_CORE_LOG_ERROR(info_log.data());
		NO7_CORE_ASSERT(false, "Vertex shader compilation failure!");
		return;
	}
	GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
	source = fragment_source_code.c_str();
	glShaderSource(fragment_shader, 1, &source, 0);
	glCompileShader(fragment_shader);
	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &if_compiled);
	if (if_compiled == GL_FALSE)
	{
		GLint max_length = 0;
		glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &max_length);
		std::vector<GLchar> info_log(max_length);
		glGetShaderInfoLog(fragment_shader, max_length, &max_length, &info_log[0]);
		glDeleteShader(vertex_shader);
		glDeleteShader(fragment_shader);
		NO7_CORE_LOG_ERROR(info_log.data());
		NO7_CORE_ASSERT(false, "Fragment shader compilation failure!");
		return;
	}
	GLuint program = glCreateProgram();
	glAttachShader(program, vertex_shader);
	glAttachShader(program, fragment_shader);
	glLinkProgram(program);
	GLint if_linked = 0;
	glGetProgramiv(program, GL_LINK_STATUS, (int*)&if_linked);
	if (if_linked == GL_FALSE)
	{
		GLint max_length = 0;
		std::vector<GLchar> info_log(max_length);
		glGetProgramInfoLog(program, max_length, &max_length, &info_log[0]);
		glDeleteProgram(program);
		glDeleteShader(vertex_shader);
		glDeleteShader(fragment_shader);
		NO7_CORE_LOG_ERROR(info_log.data());
		NO7_CORE_ASSERT(false, "Shader link failure!");
		return;
	}
	glDetachShader(program, vertex_shader);
	glDetachShader(program, fragment_shader);
	this->program_id = program;
}

void nmspc::Shader::bind() const
{
	glUseProgram(this->program_id);
}

void nmspc::Shader::unBind() const
{
	glUseProgram(0);
}

void nmspc::Shader::uploadUniformMat4(const std::string& name, const glm::mat4& matrix)
{
	GLint location = glGetUniformLocation(this->program_id, name.c_str());
	glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
}
